// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ****************************************************************************
//  avtAVSucdFileFormat.C
// ****************************************************************************

#include <avtAVSucdFileFormat.h>


#include <vtkUnstructuredGrid.h>
#include <vtkCellTypes.h>
#include <vtkCellData.h>
#include <vtkPointData.h>

#include <avtDatabaseMetaData.h>

#include <InvalidDBTypeException.h>
#include <InvalidVariableException.h>

#include <VisItAVSucdReader.h>

#include <vector>

using     std::map;
using     std::string;
using     std::vector;


// ****************************************************************************
//  Method: avtAVSucdFileFormat constructor
//
//  Programmer: biagas2 -- generated by xml2avt
//  Creation:   Tue Feb 4 11:14:47 PDT 2020
//
// ****************************************************************************

avtAVSucdFileFormat::avtAVSucdFileFormat(const char *filename)
    : avtSTSDFileFormat(filename)
{
    reader = NULL;
    haveReadInData = false;
}


// ****************************************************************************
//  Method: avtAVSucdFileFormat destructor
//
//  Programmer: biagas2 -- generated by xml2avt
//  Creation:   Tue Feb 4 11:14:47 PDT 2020
//
// ****************************************************************************

avtAVSucdFileFormat::~avtAVSucdFileFormat()
{
    if (reader)
    {
        reader->Delete();
        reader=NULL;
    }
}


// ****************************************************************************
//  Method: avtAVSucdFileFormat::FreeUpResources
//
//  Purpose:
//      When VisIt is done focusing on a particular timestep, it asks that
//      timestep to free up any resources (memory, file descriptors) that
//      it has associated with it.  This method is the mechanism for doing
//      that.
//
//  Programmer: biagas2 -- generated by xml2avt
//  Creation:   Tue Feb 4 11:14:47 PDT 2020
//
// ****************************************************************************

void
avtAVSucdFileFormat::FreeUpResources()
{
    if (reader)
    {
        reader->Delete();
        reader=NULL;
    }
    haveReadInData = false;
}


// ****************************************************************************
//  Method: avtAVSucdFileFormat::PopulateDatabaseMetaData
//
//  Purpose:
//      This database meta-data object is like a table of contents for the
//      file.  By populating it, you are telling the rest of VisIt what
//      information it can request from you.
//
//  Programmer: biagas2 -- generated by xml2avt
//  Creation:   Tue Feb 4 11:14:47 PDT 2020
//
// ****************************************************************************

void
avtAVSucdFileFormat::PopulateDatabaseMetaData(avtDatabaseMetaData *md)
{
    ReadInData();
    if(reader->GetNumberOfNodes() == 0)
        EXCEPTION1(InvalidDBTypeException, filename);

    vtkUnstructuredGrid *ugrid = reader->GetOutput();

    double bounds[6];
    ugrid->GetBounds(bounds);

    string mesh("Mesh");
    avtMeshType mt = AVT_UNSTRUCTURED_MESH;

    int nblocks = 1;
    int block_origin = 0;
    int spatial_dimension = 3;
    int topological_dimension = reader->GetTopologicalDimension();

    if ((bounds[4] == bounds[5]) && (bounds[5] == 0.))
    {
        spatial_dimension = 2;
    }

    AddMeshToMetaData(md, mesh, mt, &bounds[0], nblocks,
                      block_origin, spatial_dimension, topological_dimension);

    vector<string> scalarNames;
    vector<string> vectorNames;
    vector<int>    vectorLengths;

    for(int i = 0; i < reader->GetNumberOfNodeComponents(); ++i)
    {
        string name(reader->GetPointArrayName(i));
        int nComp = reader->GetPointArrayVecLength(i);
        if (nComp == 1)
            scalarNames.push_back(name);
        else
        {
            vectorNames.push_back(name);
            vectorLengths.push_back(nComp);
        }
        nodeCentered[name] = true;
    }

    for(int i = 0; i < reader->GetNumberOfCellComponents(); ++i)
    {
        string name(reader->GetCellArrayName(i));
        int nComp = reader->GetCellArrayVecLength(i);
        if (nComp == 1)
        {
            vector<string>::iterator it = std::find(scalarNames.begin(), scalarNames.end(), name);
            if (it == scalarNames.end())
            {
                scalarNames.push_back(name);
                nodeCentered[name] = false;
            }
            else
            {
                // rename the node-centered variable
                // first removing it from the nodeCentered map.
                nodeCentered.erase(*it);
                *it += "_nodal";
                nodeCentered[*it] = true;

                // rename the cell-centered variable
                name += "_zonal" ;
                scalarNames.push_back(name);
                nodeCentered[name] = false;
            }
        }
        else
        {
            vector<string>::iterator it = std::find(vectorNames.begin(), vectorNames.end(), name);
            if (it == vectorNames.end())
            {
                vectorNames.push_back(name);
                vectorLengths.push_back(nComp);
                nodeCentered[name] = false;
            }
            else
            {
                // rename the node-centered variable
                // first removing it from the nodeCentered map.
                nodeCentered.erase(*it);
                *it += "_nodal";
                nodeCentered[*it] = true;

                // rename the cell-centered variable
                name += "_zonal" ;
                vectorNames.push_back(name);
                vectorLengths.push_back(nComp);
                nodeCentered[name] = false;
            }
        }

    }

    for (size_t i = 0; i < scalarNames.size(); ++i)
    {
        string name = scalarNames[i];

        size_t pos = name.find("[0]");
        if(pos != std::string::npos)
        {
            bool addNodal = name.find("_nodal") != std::string::npos;
            bool addZonal = name.find("_zonal") != std::string::npos;

            string baseV = name.substr(0, pos);

            string v1(baseV + "[1]");
            if (addNodal)
                v1 += "_nodal";
            else if (addZonal)
                v1 += "_zonal";
            bool foundJ = std::find(scalarNames.begin(), scalarNames.end(), v1) != scalarNames.end();

            string v2(baseV + "[2]");
            if (addNodal)
                v2 += "_nodal";
            else if (addZonal)
                v2 += "_zonal";
            bool foundK = std::find(scalarNames.begin(), scalarNames.end(), v2) != scalarNames.end();

            if(foundJ)
            {
                // need to convert [x] to _lb_x_rb_ for use with expressions
                string v0 = baseV + "_lb_0_rb_";
                v1 = baseV + "_lb_1_rb_";
                v2 = baseV + "_lb_2_rb_";
                if (addNodal)
                {
                    v0 += "_nodal";
                    v1 += "_nodal";
                    v2 += "_nodal";
                }
                else if (addZonal)
                {
                    v0 += "_zonal";
                    v1 += "_zonal";
                    v2 += "_zonal";
                }
                string defn("{" +v0 + ", " + v1);
                if(foundK)
                    defn += string(", " + v2);
                defn += "}";

                Expression vecExpr;
                vecExpr.SetName(baseV);
                vecExpr.SetDefinition(defn);
                vecExpr.SetType(Expression::VectorMeshVar);
                md->AddExpression(&vecExpr);
            }
        }

        if(nodeCentered[name])
            AddScalarVarToMetaData(md, name, mesh, AVT_NODECENT);
        else
            AddScalarVarToMetaData(md, name, mesh, AVT_ZONECENT);
    }

    for (size_t i = 0; i < vectorNames.size(); ++i)
    {
        if(nodeCentered[vectorNames[i]])
            AddVectorVarToMetaData(md, vectorNames[i], mesh, AVT_NODECENT, vectorLengths[i]);
        else
            AddVectorVarToMetaData(md, vectorNames[i], mesh, AVT_ZONECENT, vectorLengths[i]);
    }

    AddScalarVarToMetaData(md, "Material Id", mesh, AVT_ZONECENT);
}


// ****************************************************************************
//  Method: avtAVSucdFileFormat::GetMesh
//
//  Purpose:
//      Gets the mesh associated with this file.  The mesh is returned as a
//      derived type of vtkDataSet (ie vtkRectilinearGrid, vtkStructuredGrid,
//      vtkUnstructuredGrid, etc).
//
//  Arguments:
//      meshname    The name of the mesh of interest.  This can be ignored if
//                  there is only one mesh.
//
//  Programmer: biagas2 -- generated by xml2avt
//  Creation:   Tue Feb 4 11:14:47 PDT 2020
//
// ****************************************************************************

vtkDataSet *
avtAVSucdFileFormat::GetMesh(const char *meshname)
{
    ReadInData();
    vtkDataSet *mesh = reader->GetOutput();
    mesh->Register(NULL);
    return mesh;
}


// ****************************************************************************
//  Method: avtAVSucdFileFormat::GetVar
//
//  Purpose:
//      Gets a scalar variable associated with this file.
//
//  Arguments:
//      varname    The name of the variable requested.
//
//  Programmer: biagas2 -- generated by xml2avt
//  Creation:   Tue Feb 4 11:14:47 PDT 2020
//
// ****************************************************************************

vtkDataArray *
avtAVSucdFileFormat::GetVar(const char *varname)
{
    ReadInData();
    string name(varname);

    // If this reader added "_nodal" or "_zonal", then need to remove it before
    // attempting to retrieve from the dataset.

    size_t pos = name.find("_nodal");
    if (pos != std::string::npos)
    {
        name = name.substr(0, pos);
    }
    else
    {
        pos = name.find("_zonal");
        if (pos != std::string::npos)
            name = name.substr(0, pos);
    }

    vtkDataArray *arr = NULL;
    if (nodeCentered[varname])
        arr = reader->GetPointArray(name.c_str());
    else
        arr = reader->GetCellArray(name.c_str());

    if (arr == NULL)
    {
        EXCEPTION1(InvalidVariableException, varname);
    }

    arr->Register(NULL);
    return arr;
}


// ****************************************************************************
//  Method: avtAVSucdFileFormat::GetVectorVar
//
//  Purpose:
//      Gets a vector variable associated with this file.
//
//  Arguments:
//      varname    The name of the variable requested.
//
//  Programmer: biagas2 -- generated by xml2avt
//  Creation:   Tue Feb 4 11:14:47 PDT 2020
//
// ****************************************************************************

vtkDataArray *
avtAVSucdFileFormat::GetVectorVar(const char *varname)
{
    return GetVar(varname);
}


// ****************************************************************************
//  Method: avtAVSucdFileFormat::ReadInData
//
//  Purpose:
//      Reads in the data.
//
//  Programmer: Kathleen Biagas
//  Creation:   Tue Feb 4 11:14:47 PDT 2020
//
// ****************************************************************************

void
avtAVSucdFileFormat::ReadInData()
{
    if (!haveReadInData)
    {
        if (reader != NULL)
            reader->Delete();

        reader = VisItAVSucdReader::New();
        reader->SetFileName(filename);
        reader->Update();
        haveReadInData = true;
    }
}

